/****************************************************
	文件：RObject.cs
	作者：HuskyT
	邮箱：1005240602@qq.com
	日期：2021/1/9 17:25:38
	功能：Object 加载器
*****************************************************/

using System;
using System.Collections.Generic;
using HTFW.Core;
using HTFW.Logger;
using IDisposable = HTFW.Core.IDisposable;
using Object = UnityEngine.Object;

namespace HTFW.Res
{
    public class RObject : IInitializable, IDisposable
    {
        /// <summary>
        /// Object 与其对应资源之间的映射（key：Object的InstanceID）
        /// </summary>
        Dictionary<int, ResData> mMap;

        /// <summary>
        /// 加载出来的 Object 的引用计数（key：Object的InstanceID）
        /// </summary>
        Dictionary<int, int> mObjRefDict;

        HashSet<string> mPreloadSet;

        IContext mResContext;

        public RObject(IContext context)
        {
            mResContext = context;
            mResContext.OnInit += (resContext) => Init();
            mResContext.OnDispose += (resContext) => Dispose();
        }

        public void Init(params object[] args)
        {
            mMap = new Dictionary<int, ResData>();
            mObjRefDict = new Dictionary<int, int>();
            mPreloadSet = new HashSet<string>();
        }

        public void Dispose()
        {
            ClearAll();
            mMap = null;
            mObjRefDict = null;
            mPreloadSet = null;
        }

        /// <summary>
        /// 同步加载
        /// </summary>
        /// <param name="path"></param>
        /// <param name="isSprite"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T LoadSync<T>(string path, bool isSprite = false) where T : Object
        {
            ResData assetRes = ResService.Instance.LoadSync(path, isSprite);

            if (mPreloadSet.Contains(path))
            {
                assetRes.DecreaseRef();
                mPreloadSet.Remove(path);
            }

            T obj = assetRes.Obj as T;

            IncreaseObjRef(obj, assetRes, 1);

            return obj;
        }

        /// <summary>
        /// 异步加载
        /// </summary>
        /// <param name="path"></param>
        /// <param name="priority"></param>
        /// <param name="onCompleted"></param>
        /// <param name="isSprite"></param>
        /// <typeparam name="T"></typeparam>
        public void LoadAsync<T>(string path, ResTaskPriority priority, Action<T> onCompleted, bool isSprite = false)
            where T : Object
        {
            T obj;
            if (mPreloadSet.Contains(path))
            {
                mPreloadSet.Remove(path);
                if (ResCache.Instance.TryGet(path, out ResData assetRes))
                {
                    obj = assetRes.Obj as T;
                    IncreaseObjRef(obj, assetRes, 1);
                    onCompleted?.Invoke(obj);
                }
                else
                {
                    HLog.Error($"LoadAsync error，{path}尚未加载，检查是否重复卸载 or 预加载失败");
                }

                return;
            }

            ResService.Instance.LoadAsync(path, priority, (assetRes) =>
            {
                obj = assetRes.Obj as T;
                IncreaseObjRef(obj, assetRes, 1);
                onCompleted?.Invoke(obj);
            }, isSprite);
        }

        /// <summary>
        /// 预加载到内存（同步）
        /// </summary>
        /// <param name="path"></param>
        /// <param name="isSprite"></param>
        /// <typeparam name="T"></typeparam>
        public void PreloadSync<T>(string path, bool isSprite = false) where T : Object
        {
            if (ResCache.Instance.Contains(path)) return;
            ResService.Instance.LoadSync(path, isSprite);
            mPreloadSet.Add(path);
        }

        /// <summary>
        /// 预加载到内存（异步）
        /// </summary>
        /// <param name="path"></param>
        /// <param name="priority"></param>
        /// <param name="onCompleted"></param>
        /// <param name="isSprite"></param>
        /// <typeparam name="T"></typeparam>
        public void PreloadAsync<T>(string path, ResTaskPriority priority, Action<T> onCompleted, bool isSprite = false)
            where T : Object
        {
            if (ResCache.Instance.Contains(path)) return;
            ResService.Instance.LoadAsync(path, priority, (assetRes) =>
            {
                mPreloadSet.Add(path);
                onCompleted?.Invoke(assetRes.Obj as T);
            }, isSprite);
        }

        /// <summary>
        /// 卸载
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="clearMemory"></param>
        /// <typeparam name="T"></typeparam>
        public void Unload<T>(T obj, bool clearMemory = false) where T : Object
        {
            int uid = obj.GetInstanceID();
            if (DecreaseObjRef(uid, 1))
            {
                if (mMap.TryGetValue(uid, out ResData assetRes))
                {
                    assetRes.IsClearMemory = clearMemory;
                    ResService.Instance.Unload(assetRes);
                }
            }
        }

        /// <summary>
        /// 清除所有预加载的资源
        /// </summary>
        public void ClearPreload()
        {
            foreach (var path in mPreloadSet)
            {
                if (ResCache.Instance.TryGet(path, out ResData assetRes))
                {
                    assetRes.IsClearMemory = true;
                    ResService.Instance.Unload(assetRes);
                }
            }

            mPreloadSet.Clear();
        }

        /// <summary>
        /// 清除非预加载资源（防止引用丢失导致内存泄漏）
        /// </summary>
        /// <param name="clearMemory"></param>
        public void ClearInstance(bool clearMemory)
        {
            foreach (var objRef in mObjRefDict)
            {
                if (objRef.Value <= 0) continue;
                ResData assetRes = mMap[objRef.Key];
                assetRes.IsClearMemory = clearMemory;
                for (int i = 0; i < objRef.Value; i++)
                {
                    ResService.Instance.Unload(assetRes);
                }
            }
        }

        /// <summary>
        /// 清除所有资源内存
        /// </summary>
        public void ClearAll()
        {
            ClearPreload();
            ClearInstance(true);
        }


        /// <summary>
        /// 增加 Object 的引用计数
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="assetRes"></param>
        /// <param name="addCount"></param>
        /// <typeparam name="T"></typeparam>
        void IncreaseObjRef<T>(T obj, ResData assetRes, int addCount) where T : Object
        {
            int uid = obj.GetInstanceID();
            if (mObjRefDict.ContainsKey(uid))
            {
                mObjRefDict[uid] += addCount;
            }
            else
            {
                mObjRefDict.Add(uid, addCount);
                mMap.Add(uid, assetRes);
            }
        }

        /// <summary>
        /// 增加 Object 的引用计数
        /// </summary>
        /// <param name="uid"></param>
        /// <param name="assetRes"></param>
        /// <param name="addCount"></param>
        void IncreaseObjRef(int uid, ResData assetRes, int addCount)
        {
            if (mObjRefDict.ContainsKey(uid))
            {
                mObjRefDict[uid] += addCount;
            }
            else
            {
                mObjRefDict.Add(uid, addCount);
                mMap.Add(uid, assetRes);
            }
        }

        /// <summary>
        /// 减少 Object 的引用计数
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="minusCount"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        bool DecreaseObjRef<T>(T obj, int minusCount) where T : Object
        {
            int uid = obj.GetInstanceID();
            if (mObjRefDict.ContainsKey(uid))
            {
                mObjRefDict[uid] -= minusCount;
                if (mObjRefDict[uid] < 0)
                {
                    HLog.Error($"重复卸载资源：{obj.name}");
                    return false;
                }
                else
                {
                    return true;
                }
            }
            else
            {
                HLog.Error($"卸载尚未加载的资源：{obj.name}");
                return false;
            }
        }

        /// <summary>
        /// 减少 Object 的引用计数
        /// </summary>
        /// <param name="uid"></param>
        /// <param name="minusCount"></param>
        /// <returns></returns>
        bool DecreaseObjRef(int uid, int minusCount)
        {
            if (mObjRefDict.ContainsKey(uid))
            {
                mObjRefDict[uid] -= minusCount;
                if (mObjRefDict[uid] < 0)
                {
                    HLog.Error($"重复卸载资源：{uid}");
                    return false;
                }
                else
                {
                    return true;
                }
            }
            else
            {
                HLog.Error($"卸载尚未加载的资源：{uid}");
                return false;
            }
        }
    }
}